// Stippled Circle problem in which a "spotlight" of a circle
// moves and appears to be a beam of light onto a wall with
// crazy wallpaper.

#ifdef __APPLE_CC__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

// The FPS of the animation
static const int FPS = 60;

// Tracks the x position of the circle
static GLfloat xPos = 0;
static const float OFFSCREEN = 11;
static const float SPEED = 0.05;

// Defines the stipple pattern (custom made by FunkaForney)
// CAUTION: Eyes *will* hurt after watching several times
static GLubyte funkyForneyTone[] = {
        0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
        0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
        0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00};

// function that creates the stipple patterns and "spotlight" effect
void display() {
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0, 0.0, 1.0);
    glEnable(GL_POLYGON_STIPPLE);
    glPolygonStipple(funkyForneyTone);

    // Draw the circle spotlight
    glPushMatrix();
        glTranslatef(-5.0 + xPos, 0.0, 0.0);
        glRotatef(90, 1.0, 0.0, 0.0);
        glutSolidSphere(1.25, 3.0, 20.0);
    glPopMatrix();

    glFlush();
    glutSwapBuffers();
}

// Set up the stippling etc.
void init() {
    glShadeModel(GL_FLAT);
}

// Parse the movement of the spotlight through a timer callback
void timer(int v) {
    xPos += SPEED;
    if (xPos > OFFSCREEN) {
        xPos = 0;
    }
    glutPostRedisplay();
    glutTimerFunc(1000 / FPS, timer, 0);
}

// Function that resets the window, each time when the window size is changed
void reshape(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    GLfloat aspect = GLfloat(w) / GLfloat(h);
    glLoadIdentity();
    if (w <= h) {
        // width is smaller, so stretch out the height
        glOrtho(-2.5, 2.5, -2.5/aspect, 2.5/aspect, -10.0, 10.0);
    } else {
        // height is smaller, so stretch out the width
        glOrtho(-2.5*aspect, 2.5*aspect, -2.5, 2.5, -10.0, 10.0);
    }
}

// Main entry point of the program
int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowPosition(80, 80);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Stippling to the Max!");
    glutTimerFunc(100, timer, 0);
    glutReshapeFunc(reshape);
    glutDisplayFunc(display);
    init();
    glutMainLoop();
}
